/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

CPLDBitstreamNode::CPLDBitstreamNode(
	BuildGraph* graph,
	BuildGraphNode* netlist,
	FPGAToolchain* toolchain,
	string device)
	: BuildGraphNode(graph)
	, m_netlist(netlist)
	, m_toolchain(toolchain)
	, m_device(device)
{
	//TODO: Make this portable
	VM6NetlistNode* node = dynamic_cast<VM6NetlistNode*>(m_netlist);
	if(node == NULL)
		FatalError("CPLDBitstreamNode expects a VM6NetlistNode\n");

	m_name = node->GetName();
}

CPLDBitstreamNode::~CPLDBitstreamNode()
{
	
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factory method

CPLDBitstreamNode* CPLDBitstreamNode::CreateCPLDBitstreamNode(
		BuildGraph* graph,
		BuildGraphNode* netlist,
		FPGAToolchain* toolchain,
		string device,
		bool target)
{
	//Create the netlist node
	CPLDBitstreamNode* node = new CPLDBitstreamNode(graph, netlist, toolchain, device);
	graph->AddNodeWithPath(node, node->GetOutputPath(), device);
	
	//Add it as a top-level target if requested
	if(target)
		graph->AddTarget(node, node->m_name);
	
	//Done
	return node;
}

CPLDBitstreamNode* CPLDBitstreamNode::CreateCPLDBitstreamNode(
		BuildGraph* graph, 
		vector<string> sources,
		string name,
		string top_level,
		string device,
		string constraints,
		FPGASynthesisFlagList xstflags,
		XilinxFPGAToolchain* toolchain,
		bool target
		)
{
	NGCNetlistNode* ngc = NGCNetlistNode::CreateNGCNetlistNode(
		graph, sources, toolchain, name, top_level, device, xstflags);
	NGDNetlistNode* ngd = NGDNetlistNode::CreateNGDNetlistNode(
		graph, ngc, constraints, device, toolchain);
	VM6NetlistNode* vm6 = VM6NetlistNode::CreateVM6NetlistNode(
		graph, ngd, device, toolchain);
	//TODO: Timing analysis
	return CPLDBitstreamNode::CreateCPLDBitstreamNode(
		graph, vm6, toolchain, device, target);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

string CPLDBitstreamNode::GetOutputPath()
{
	return m_graph->GetOutDir() + "/" + m_toolchain->GetArchitecture(m_device) + "/" + m_name + ".jed";
}

string CPLDBitstreamNode::GetDescription()
{
	char desc[1024];
	snprintf(desc, sizeof(desc), "CPLD bitstream %s", m_name.c_str());
	return string(desc);
}

string CPLDBitstreamNode::GetBinaryPath()
{
	string hash = GetHashCached();
	string outdir = m_graph->GetMetaDir() + "/" + hash;
	return outdir + "/" + m_name + ".jed";
}

bool CPLDBitstreamNode::GetFilteredOutput(vector<string>& output)
{
	static const char* whitelist[]=
	{
		"ERROR",
		"WARNING"
	};
	
	static const char* blacklist[]=
	{
		"WARNING:Cpld - Unable to retrieve"//what is an iSE project repository anyway?
	};
	
	return BuildGraphNode::GetFilteredOutput(
		output,
		whitelist,
		sizeof(whitelist)/sizeof(whitelist[0]),
		blacklist,
		sizeof(blacklist)/sizeof(blacklist[0])
		);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Building

void CPLDBitstreamNode::UpdateDependencies()
{
	DebugPrintfHeading(4, "CPLDBitstreamNode::UpdateDependencies()\n");
	
	m_dependencies.clear();
	m_dependencies.push_back(m_netlist);
}

string CPLDBitstreamNode::GetHash()
{	
	DebugPrintfHeading(4, "CPLDBitstreamNode::GetHash() [node = \"%s\"]\n", m_name.c_str());
	string hashin = "";
	
	//Update our dependencies
	UpdateDependencies();

	//Add data at this node
	hashin += sha256(m_name);
	hashin += m_toolchain->GetHash();
	hashin += sha256(m_device);
	/*if(m_flags.size())
		FatalError("CPLDBitstreamNode::GetHash() does not handle flags yet\n");*/
		
	//Add data for our dependencies
	for(size_t i=0; i<m_dependencies.size(); i++)
		hashin += m_dependencies[i]->GetHashCached(); 
	
	return sha256(hashin);
}

void CPLDBitstreamNode::Update(Cluster* cluster)
{
	//Rebuild all of our dependencies if necessary
	vector<ClusterJob*> deps;
	for(size_t i=0; i<m_dependencies.size(); i++)
	{		
		BuildGraphNode* node = m_dependencies[i];
		node->UpdateCached(cluster);
		if(node->GetBuildJob() != NULL)
			deps.push_back(node->GetBuildJob());
	}
	
	//TODO: Make this portable
	VM6NetlistNode* node = dynamic_cast<VM6NetlistNode*>(m_netlist);
	if(node == NULL)
		FatalError("CPLDBitstreamNode::Update() expects a VM6NetlistNode\n");
	
	//Run the actual bitstream-generation step
	m_job = m_toolchain->GenerateCPLDBitstream(
		m_netlist->GetBinaryPath(),
		GetBinaryPath(),
		deps,
		cluster);
}
